//-------------------------------------------------------------------
//	Copyright (c) 2013-2014  Zhirnov Andrey
//	This file is part of the "UX OpwnGL Framework" project.
//	See copyright notice in "UX_GL_Framework.h".
//-------------------------------------------------------------------

#include "CLGenerator.h"
#include "../Loaders/ShaderLoader.h"

namespace Framework
{
	
/*
=================================================
	Run
=================================================
*/
	bool CLGenerator::Run  (CLQueue &q, CLKernel &k,
							const svec3 &offset, const svec3 &dim,
							const texture_args_t &textures,
							const memimage_args_t &images,
							const image_args_t &clImages)
	{
		CHECK_ARGUMENT( q.Id() != null );
		CHECK_ARGUMENT( k.Id() != null );

		array< CLImage >	cl_images( textures.Count() + images.Count() + clImages.Count() );
		array< cl_mem >		cl_mem_array( textures.Count() );

		FOR( i, cl_images )
		{
			// gl textures
			if ( i < textures.Count() )
			{
				CHECK_RETURN( cl_images[i].CreateFromTexture( textures[i].tex.ptr(), textures[i].flags ) );
				k.SetArg( textures[i].index, cl_images[i].Id() );
				
				cl_mem_array[i] = cl_images[i].Id();
			}
			else
			// memory images
			if ( i < images.Count() + textures.Count() )
			{
				const usize j = i - textures.Count();

				CHECK_RETURN( cl_images[i].Create2D( dim.xy(), images[j].flags, cl_img_format(images[j].order, images[j].channel),
													(void *)images[j].data.ptr() ) );

				k.SetArg( images[j].index, cl_images[i].Id() );
			}
			else
			// OpenCL images
			if ( i < images.Count() + textures.Count() + clImages.Count() )
			{
				const usize	j = i - (textures.Count() + clImages.Count());

				cl_images[i].SwapData( *clImages[j].image );

				k.SetArg( clImages[j].index, cl_images[i].Id() );
			}
		}

		if ( not cl_mem_array.Empty() )
		{
			GL_CALL( glFinish() );
			CHECK_RETURN( q.EnqueueAcquireGLObjects( clmem_uni_array( cl_mem_array.ptr(), cl_mem_array.Count() ) ) );
		}

		CHECK_RETURN( q.EnqueueNDRange( &k, offset, dim, svec3(0) ) );

		if ( not cl_mem_array.Empty() ) {
			CHECK_RETURN( q.EnqueueReleaseGLObjects( clmem_uni_array( cl_mem_array.ptr(), cl_mem_array.Count() ) ) );
		}
		
		FORv( i, textures.Count(), cl_images )
		{
			const usize j = i - textures.Count();

			if ( EnumCmp( images[j].flags, cl_memory::WRITE_ONLY ) or
				 EnumCmp( images[j].flags, cl_memory::READ_WRITE ) )
			{
				CHECK_RETURN( cl_images[i].EnqueueRead( &q, false, svec3(0), svec3(dim.xy(), 1), svec2(0), (void *)images[j].data.ptr() ) );
			}
		}
		return true;
	}
	
/*
=================================================
	Run
=================================================
*/
	bool CLGenerator::Run  (CLQueue &q, CLProgram &prog, uni_c_string kernelName,
							const svec3 &offset, const svec3 &dim,
							const texture_args_t &textures,
							const memimage_args_t &images,
							const image_args_t &clImages,
							const variant_args_t &args)
	{
		CHECK_ARGUMENT( prog.Id() != null );

		CLKernel	kernel;
		CHECK_RETURN( kernel.Create( &prog, kernelName ) );

		FOR( i, args )
		{
			kernel.SetArg( args[i].index, (const uint8 *)args[i].ptr, args[i].size );
		}

		return Run( q, kernel, offset, dim, textures, images, clImages );
	}
	
/*
=================================================
	Run
=================================================
*/
	bool CLGenerator::Run  (uni_c_string programName, uni_c_string preprocessor, uni_c_string options,
							uni_c_string kernelName, const svec3 &offset, const svec3 &dim,
							const texture_args_t &textures, const memimage_args_t &images,
							const image_args_t &clImages, const variant_args_t &args)
	{
		CLProgramPtr	prog;
		CHECK_RETURN( ShaderLoader::Load( prog, programName, preprocessor, options ) );

		CLQueue		cl_queue;
		CHECK_RETURN( cl_queue.Create() );

		CHECK_RETURN( Run( cl_queue, *prog, kernelName, offset, dim, textures, images, clImages, args ) );
		CHECK_RETURN( cl_queue.Finish() );

		return true;
	}

}	// Framework
